package com.thinkingcoder.tcconfig.client.value;

import java.util.List;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.cloud.context.environment.EnvironmentChangeEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;

import com.thinkingcoder.common.util.FieldUtils;
import com.thinkingcoder.tcconfig.client.util.PlaceholderHelper;

import lombok.Setter;
import lombok.extern.slf4j.Slf4j;

/**
 * process spring value.<br/>
 * 1 扫描素有的 spring value，保存起来<br/>
 * 2 在配置变更时，更新所有 spring value
 */
@Slf4j
public class SpringValueProcessor
    implements BeanPostProcessor, BeanFactoryAware, ApplicationListener<EnvironmentChangeEvent> {

    static final PlaceholderHelper placeholderHelper = PlaceholderHelper.getInstance();
    static final MultiValueMap<String, SpringValue> VALUE_HOLDER = new LinkedMultiValueMap<>();

    @Setter
    private BeanFactory beanFactory;

    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        // 第一步
        FieldUtils.findAnnotatedField(bean.getClass(), Value.class).forEach(field -> {
            log.info("[TC-CONFIG] >> Find spring value : {}", field);
            Value value = field.getAnnotation(Value.class);
            placeholderHelper.extractPlaceholderKeys(value.value()).forEach(key -> {
                log.info("[TC-CONFIG] >> Find spring value : {}", key);
                SpringValue springValue = new SpringValue(bean, beanName, key, value.value(), field);
                VALUE_HOLDER.add(key, springValue);
            });
        });

        return bean;
    }

    @Override
    public void onApplicationEvent(EnvironmentChangeEvent event) {
        log.info("[TC-CONFIG] >> Update spring value for keys : {}", event.getKeys());
        event.getKeys().forEach(key -> {
            log.info("[TC-CONFIG] >> Update spring value : {}", key);
            List<SpringValue> springValues = VALUE_HOLDER.get(key);
            if (springValues == null || springValues.isEmpty()) {
                return;
            }
            springValues.forEach(springValue -> {
                log.info("[TC-CONFIG] >> Update spring value : {} for key {}", springValue, key);
                try {
                    Object value = placeholderHelper.resolvePropertyValue((ConfigurableBeanFactory)beanFactory,
                        springValue.getBeanName(), springValue.getPlaceholder());
                    log.info("[TC-CONFIG] >> Update value : {} for holder {}", value, springValue.getPlaceholder());
                    springValue.getField().setAccessible(true);
                    springValue.getField().set(springValue.getBean(), value);
                } catch (Exception e) {
                    log.error("update spring value error.", e);
                }
            });
        });
    }
}
